package com.rslai.base.tool.servertest.command;

import com.rslai.base.tool.servertest.context.Context;
import com.rslai.base.tool.servertest.exception.ExecuteException;
import com.rslai.base.tool.servertest.model.KeyValueStore;
import com.rslai.base.tool.servertest.response.Response;
import com.rslai.base.tool.servertest.util.CloneUtil;
import com.rslai.base.tool.servertest.util.Util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;

public class CompareStepCommand extends ParameterizedCommand {
    private static final String separator = System.getProperty("line.separator", "\r\n");

    private String error = "";
    private String ignore;

    public CompareStepCommand(List<KeyValueStore> params, String ignore) {
        super(params);
        this.ignore = ignore;
    }

    protected Response doExecuteInternal(Response preResult, List<KeyValueStore> processedParams, Context context) throws Throwable {
        Map expectation = convertKeyValueStoreToMap(processedParams);
        this.logger.info("assert command<{}> is starting... ", expectation);
        Object body = preResult.getBody();
        Map actual = null;
        Map expect = null;
        if (Util.isJson(expectation.get("body"))) {
            expect = (Map) com.alibaba.fastjson.JSON.parse((String) expectation.get("body"));
        }
        if (Util.isJson(body)) {
            actual = (Map) com.alibaba.fastjson.JSON.parse((String) body);
        }
        if ((StringUtils.isNotBlank(this.ignore)) && (actual != null) && (expect != null)) {
            processIgnores(actual, expect);
        }
        expectation.put("body", com.alibaba.fastjson.JSON.toJSONString(expect));
        try {
            preResult.verify(expectation);
            return preResult;
        } catch (Exception e) {
            String message = "assert step invoke has error,expect=" + expectation + separator + "result=" + preResult;
            this.logger.error(message, e);
            this.error = com.rslai.base.tool.autotest.mock.util.JSON.toJSONString(preResult);
            throw new ExecuteException(message, e);
        }
    }

    private void processIgnores(Map<String, String> body, Map<String, String> expection) {
        List ignoreList = Arrays.asList(StringUtils.split(this.ignore, ","));
        String ignore;
        for (Iterator i$ = ignoreList.iterator(); i$.hasNext(); processIgnore(expection, ignore)) {
            ignore = (String) i$.next();
            processIgnore(body, ignore);
        }
    }

    private void processIgnore(Map<String, String> map, String ignore) {
        if ((map == null) || (StringUtils.isBlank(ignore))) return;
        String[] array = StringUtils.split(ignore, ".");
        int i = 0;
        int count = array.length;
        traversalMap(map, array, i, count);
    }

    private void traversalMap(Map<String, String> map, String[] array, int index, int count) {
        Iterator iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry entry = (Map.Entry) iterator.next();
            if (entry.getKey().equals(array[index])) {
                if (index < count - 1)
                    index++;
                else if (index == count - 1) {
                    iterator.remove();
                }
            }
            if ((entry.getValue() instanceof Map))
                traversalMap((Map) entry.getValue(), array, index, count);
        }
    }

    protected StepCommand doClone() {
        return new CompareStepCommand(CloneUtil.cloneKeyValueStore(this.params), this.ignore);
    }

    public Map<String, Object> toReport() {
        Map details = new HashMap();
        details.put("stepName", "验证:");
        List params = new ArrayList();
        params.addAll(this.params);

        params.add(new KeyValueStore("实际值", this.error));

        details.put("params", params);
        return details;
    }

    private Map<String, String> convertKeyValueStoreToMap(List<KeyValueStore> params) {
        Map result = new HashMap();
        for (KeyValueStore kvs : params) {
            Object value = kvs.getValue();
            if ((value instanceof Map))
                result.putAll((Map) value);
            else {
                result.put(kvs.getName(), (String) value);
            }
        }
        return result;
    }
}
